﻿using FFmpeg.AutoGen;
using MetroFramework.Forms;
using NAudio.Wave;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using VideoPlayer.FFmpegHandle;

namespace VideoPlayer
{
    public partial class MainFrm : MetroForm
    {
        public MainFrm()
        {
            InitializeComponent();
            //激活双缓冲技术
            SetStyle(ControlStyles.UserPaint, true);
            SetStyle(ControlStyles.AllPaintingInWmPaint, true);
            SetStyle(ControlStyles.DoubleBuffer, true);
        }
        protected override CreateParams CreateParams
        {
            get
            {
                CreateParams cp = base.CreateParams;
                cp.ExStyle |= 0x02000000;
                return cp;
            }
        }
        //播放状态
        private bool isPlaying = false;
        //缓存时间（ms）
        const int BUFFER_DURATION = 1000;
        //NAudio音频播放组件
        private WaveOut waveOut;
        private BufferedWaveProvider bufferedWaveProvider;

        [DllImport("psapi.dll")]
        static extern int EmptyWorkingSet(IntPtr hwProc);

        /// <summary>
        /// 释放内存
        /// </summary>
        public static void ClearMemory()
        {
            GC.Collect();
            GC.WaitForPendingFinalizers();
            Process[] processes = Process.GetProcesses();
            foreach (Process process in processes)
            {
                //对于系统进程会拒绝访问，导致出错，此处对异常不进行处理。
                try
                {
                    EmptyWorkingSet(process.Handle);
                }
                catch
                {
                }
            }
        }

        /// <summary>
        /// 
        /// </summary>
        BottomPlay bottomplay1;
        private void MainFrm_Load(object sender, EventArgs e)
        {
            //this.WindowState = FormWindowState.Maximized;
            FFmpegBinariesHelper.RegisterFFmpegBinaries();
            waveOut = new WaveOut();
            bufferedWaveProvider = new BufferedWaveProvider(new WaveFormat());
            waveOut.Init(bufferedWaveProvider);
            waveOut.Play();

            picVideo.AllowDrop = true;

            bottomplay1 = new BottomPlay();
            picVideo.Controls.Add(bottomplay1);
            //bottomplay1.sendtoback();
            bottomplay1.BringToFront();
            bottomplay1.Anchor = AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right;
            bottomplay1.Width = picVideo.Width;
            bottomplay1.Location = new Point(0, picVideo.Height - bottomplay1.Height);

            bottomplay1.StartOrStopAction = new Action<bool>(p =>
            {
                isPlaying = !p;
                //第一次开始
                if ((thAudioPlayer == null || thAudioPlayer.ThreadState != System.Threading.ThreadState.Running) && isPlaying)
                {
                    //if (streamDecoder == null || vfc == null)
                    //{
                    //    picVideo.Image = Properties.Resources.fail;
                    //    isPlaying = false;
                    //    bottomplay1.Stop();
                    //}
                    //else
                    DeCoding();
                }

            });
            bottomplay1.SkipTimeAction = new Action<int>(p =>
            {
                Data.audios.Clear();
                Data.bitmaps.Clear();
                streamDecoder.JumpDate = p;
            });

            Data.PicSize = picVideo.Size;
            Thread thread = new Thread(BottomPlayMove);
            thread.IsBackground = true;
            thread.Start(bottomplay1);

        }

        #region 播放

        #region 参数

        /// <summary>
        /// 视频播放线程
        /// </summary>
        Thread thVideoPlayer;

        /// <summary>
        /// 声音播放线程
        /// </summary>
        Thread thAudioPlayer;

        /// <summary>
        /// 缓存线程
        /// </summary>
        Thread thCollection;

        /// <summary>
        /// 视频流解码器
        /// </summary>
        StreamDecoder streamDecoder = null;

        /// <summary>
        /// 视频画面转换
        /// </summary>
        VideoFrameConverter vfc = null;

        /// <summary>
        /// 音频最大缓冲时间
        /// </summary>
        TimeSpan audioMaxBufferedDuration = new TimeSpan(0, 0, 0, 0, BUFFER_DURATION); //音频最大缓冲时间;

        /// <summary>
        /// 视频地址
        /// </summary>
        private string VideoPath = "";

        /// <summary>
        /// 画面大小
        /// </summary>
        Size FrameSize = new Size(0, 0);

        /// <summary>
        /// 转换后的图片大小
        /// </summary>
        Size ImageSize = new Size(1, 1);

        /// <summary>
        /// 当前播放时间 毫秒
        /// </summary>
        long indexTime = 0;


        /// <summary>
        /// 基地址时间戳
        /// </summary>
        long BaseTimeStamp = 0;

        #endregion

        #region 方法

        /// <summary>
        /// 播放线程执行方法
        /// </summary>
        private void DeCoding()
        {
            thCollection = new Thread(collection);
            thCollection.IsBackground = true;
            thCollection.Start();
            thVideoPlayer = new Thread(videPlayer);
            thVideoPlayer.IsBackground = true;
            thVideoPlayer.Start();

            thAudioPlayer = new Thread(audioPlayer);
            thAudioPlayer.IsBackground = true;
            thAudioPlayer.Start();

        }

        /// <summary>
        /// 采集
        /// </summary>
        unsafe void collection()
        {
            if (streamDecoder == null || vfc == null)
                InitVideo(VideoPath.ToString());

            while (true)
            {
                if (!isPlaying)
                {
                    Thread.Sleep(1);
                    continue;
                }
                if (Data.bitmaps.Count() > 100)
                {
                    ClearMemory();
                    Thread.Sleep(1);
                }
                int i = streamDecoder.read_frame(); //读取一帧 1:音频 2:视频
                if (i == 1)
                {

                    IntPtr audioFrame = (IntPtr)streamDecoder.get_audio_frame(); //一帧音频句柄
                    int bufSize = streamDecoder.get_audio_buffer_size(); //一帧音频占用大小
                    byte[] bytes = new byte[bufSize];
                    Marshal.Copy(audioFrame, bytes, 0, bufSize); //拷贝句柄中的音频到bytes
                    Data.audios.Enqueue(new KeyValuePair<long, byte[]>(streamDecoder.indexTime, bytes));
                    //bufferedWaveProvider.AddSamples(bytes, 0, bytes.Length);//向缓存中添加音频样本 

                }
                else if (i == 2)
                {
                    try
                    {
                        var frame = streamDecoder.get_video_frame();
                        var img = vfc.ConvertBitmap(frame);
                        Bitmap bit = new Bitmap(img.Width, img.Height);
                        using (Graphics g = Graphics.FromImage(bit))
                        {
                            g.DrawImage(img, new Point(0, 0));
                            g.DrawString("TimeStamp:" + streamDecoder.indexTime, new Font("宋体", 9f), Brushes.Red, new PointF(10, 10));
                        }
                        img.Dispose();
                        //img.Save(AppDomain.CurrentDomain.BaseDirectory + $@"img\{streamDecoder.indexTime}.jpg");
                        Data.bitmaps.Enqueue(new KeyValuePair<long, Bitmap>(streamDecoder.indexTime, bit));
                    }
                    catch (Exception ex)
                    { }
                }
                else if (i == 4)
                {

                }

            }
        }

        /// <summary>
        /// 初始化视频
        /// </summary>
        /// <param name="url">视频地址</param>
        private unsafe void InitVideo(string url)
        {
            if (string.IsNullOrWhiteSpace(url))
                url = "rtmp://58.200.131.2:1935/livetv/dftv";
            streamDecoder = new StreamDecoder(url);
            FrameSize = streamDecoder.FrameSize;
            var sourcePixelFormat = streamDecoder.PixelFormat;
            ImageSize = CalculationSize(FrameSize);
            var destinationPixelFormat = AVPixelFormat.AV_PIX_FMT_BGR24;
            vfc = new VideoFrameConverter(FrameSize, sourcePixelFormat, ImageSize, destinationPixelFormat);
            //audioMaxBufferedDuration = new TimeSpan(0, 0, 0, 0, BUFFER_DURATION); //音频最大缓冲时间
            if (bottomplay1.InvokeRequired)
            {
                bottomplay1.Invoke(new Action<int>(p =>
                {
                    bottomplay1.SetMaxTime(p);
                }), (int)streamDecoder.m_Duration / 1000);
            }
            else
            {
                bottomplay1.SetMaxTime((int)streamDecoder.m_Duration / 1000);
            }
        }

        /// <summary>
        /// 音频播放
        /// </summary>
        private void audioPlayer()
        {
            int sleepCount = 0;
            if (streamDecoder == null || vfc == null)
                InitVideo(VideoPath.ToString());
            //上一个没有插入的元素
            byte[] last = null;
            while (true)
            {
                if (!isPlaying)
                {
                    Thread.Sleep(1);
                    continue;
                }
                if (bufferedWaveProvider.BufferedDuration.CompareTo(audioMaxBufferedDuration) > 0)
                {
                    bufferedWaveProvider.ClearBuffer();
                }
                if (Data.audios.Count > 0)
                {
                    sleepCount = 0;
                    if (last != null)
                        bufferedWaveProvider.AddSamples(last, 0, last.Length);//向缓存中添加音频样本 
                    var dic_a = Data.audios.Dequeue();
                    var bytes = dic_a.Value;
                    bufferedWaveProvider.AddSamples(bytes, 0, bytes.Length);//向缓存中添加音频样本 

                    indexTime = dic_a.Key;
                    if (bottomplay1.InvokeRequired)
                    {
                        bottomplay1.Invoke(new Action<int>(p =>
                        {
                            bottomplay1.SetIndexTime(p);
                        }), (int)(indexTime / 1000));
                    }
                    else
                    {
                        bottomplay1.SetIndexTime((int)(indexTime / 1000));
                    }

                    long time = dic_a.Key - (GetTimeStamp() - BaseTimeStamp);//当前时间与基时间 差 小于 音频时间 则休眠

                    if (time > 0)
                        Thread.Sleep((int)time);
                }
                else
                {
                    sleepCount++;
                    if (sleepCount * 100 > 60000)
                    {
                        Console.WriteLine($"音频退出");
                        return;
                    }
                    Thread.Sleep(100);
                }
            }
        }

        /// <summary>
        /// 视频播放
        /// </summary>
        private void videPlayer()
        {
            int sleepCount = 0;
            if (streamDecoder == null || vfc == null)
                InitVideo(VideoPath.ToString());
            BaseTimeStamp = GetTimeStamp();
            while (true)
            {
                if (!isPlaying)
                {
                    Thread.Sleep(1);
                    continue;
                }
                if (Data.bitmaps.Count > 0)
                {
                    sleepCount = 0;
                    Stopwatch stopwatch = new Stopwatch();
                    stopwatch.Start();
                    var dic_v = Data.bitmaps.Dequeue();
                    Bitmap bitmap = dic_v.Value;
                    if (picVideo.InvokeRequired)
                    {
                        picVideo.Invoke(new Action<Bitmap>(p =>
                        {
                            picVideo.Image = p;
                        }), bitmap);
                    }
                    else
                    {
                        picVideo.Image = bitmap;
                    }
                    //bitmap.Save(AppDomain.CurrentDomain.BaseDirectory + $@"img\{dic_v.Key}.jpg");
                    stopwatch.Stop();
                    long time = dic_v.Key - (GetTimeStamp() - BaseTimeStamp);//当前时间与基时间 差 小于 音频时间 则休眠
                    //Console.WriteLine($"视频时间：{dic_v.Key},差值：{time}, 当前时间戳：{dic_v.Key - time}");
                    if (time > 0)//当前画面时间 小于 当前时间 才可以播放
                    {
                        if (time - stopwatch.ElapsedMilliseconds > 0)
                            Thread.Sleep((int)(time - stopwatch.ElapsedMilliseconds));
                    }

                }
                else
                {
                    sleepCount++;
                    if (sleepCount * 100 > 60000)
                    {
                        Console.WriteLine($"视频退出");
                        return;
                    }
                    Thread.Sleep(100);
                }
            }
        }
        #endregion

        #endregion

        #region 事件


        /// <summary>
        /// 鼠标移动
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void PicVideo_MouseMove(object sender, MouseEventArgs e)
        {
            Data.IndexBottomPlayState = Data.BottomPlayState.up;
            Data.NoMoveTime = 0;
        }

        /// <summary>
        /// 控件移动
        /// </summary>
        private void BottomPlayMove(object control)
        {
            BottomPlay bottomPlay1 = control as BottomPlay;
            int size = 15;
            while (true)
            {
                switch (Data.IndexBottomPlayState)
                {
                    case Data.BottomPlayState.stop:
                        if (Data.NoMoveTime < 0)
                            break;
                        Data.NoMoveTime += 50;
                        if (Data.NoMoveTime > Data.OverTime)
                        {
                            picVideo.MouseMove -= PicVideo_MouseMove;
                            Data.IndexBottomPlayState = Data.BottomPlayState.down;
                        }
                        break;
                    case Data.BottomPlayState.up:
                        if ((Data.PicSize.Height - bottomPlay1.Height) > (bottomPlay1.Location.Y - size))
                        {
                            ControlMove(bottomPlay1, Data.Direction.up, bottomPlay1.Location.Y - (Data.PicSize.Height - bottomPlay1.Height));
                            Data.IndexBottomPlayState = Data.BottomPlayState.stop;
                        }
                        else
                            ControlMove(bottomPlay1, Data.Direction.up, size);
                        break;
                    case Data.BottomPlayState.down:
                        if ((bottomPlay1.Location.Y + size) > Data.PicSize.Height)
                        {
                            ControlMove(bottomPlay1, Data.Direction.down, Data.PicSize.Height - bottomPlay1.Location.Y);
                            Data.IndexBottomPlayState = Data.BottomPlayState.stop;
                            picVideo.MouseMove += PicVideo_MouseMove;
                            Data.NoMoveTime = -100;
                        }
                        else
                            ControlMove(bottomPlay1, Data.Direction.down, size);
                        break;
                    default:
                        break;
                }
                Thread.Sleep(50);

            }

        }

        /// <summary>
        /// 控件移动
        /// </summary>
        /// <param name="control"></param>
        /// <param name=""></param>
        /// <param name="size"></param>
        private void ControlMove(Control control, Data.Direction direction, int size)
        {
            int temp = 0;
            switch (direction)
            {
                case Data.Direction.up:
                    temp = control.Location.Y - size;
                    if (control.InvokeRequired)
                        control.Invoke(new Action<int>((p) =>
                        {
                            control.Location = new Point(control.Location.X, p);
                        }), temp);
                    else
                        control.Location = new Point(control.Location.X, temp);
                    break;
                case Data.Direction.down:
                    temp = control.Location.Y + size;
                    if (control.InvokeRequired)
                        control.Invoke(new Action<int>((p) =>
                        {
                            control.Location = new Point(control.Location.X, p);
                        }), temp);
                    else
                        control.Location = new Point(control.Location.X, temp);
                    break;
                case Data.Direction.left:
                    temp = control.Location.X + size;
                    if (control.InvokeRequired)
                        control.Invoke(new Action<int>((p) =>
                        {
                            control.Location = new Point(p, control.Location.Y);
                        }), temp);
                    else
                        control.Location = new Point(temp, control.Location.Y);
                    break;
                case Data.Direction.right:
                    temp = control.Location.X - size;
                    if (control.InvokeRequired)
                        control.Invoke(new Action<int>((p) =>
                        {
                            control.Location = new Point(p, control.Location.Y);
                        }), temp);
                    else
                        control.Location = new Point(temp, control.Location.Y);
                    break;
                default:
                    break;
            }
        }

        /// <summary>
        /// 文件拖到picbox结束
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void PicVideo_DragDrop(object sender, DragEventArgs e)
        {
            isPlaying = false;
            Thread.Sleep(50);
            picVideo.Image = Properties.Resources.loading;
            if (thAudioPlayer != null && thAudioPlayer.ThreadState == System.Threading.ThreadState.Running)
            {
                try
                {
                    thCollection.Abort();
                    thAudioPlayer.Abort();
                    thVideoPlayer.Abort();
                }
                catch { }
            }
            thCollection = null;
            thAudioPlayer = null;
            thVideoPlayer = null;

            streamDecoder = null;
            vfc = null;
            VideoPath = ((System.Array)e.Data.GetData(DataFormats.FileDrop)).GetValue(0).ToString();
            try
            {
                InitVideo(VideoPath);
                picVideo.Image = Properties.Resources.loading_ok;
            }
            catch (Exception ex)
            {
                picVideo.Image = Properties.Resources.fail;
            }

        }

        /// <summary>
        /// 文件拖到picbox上
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void PicVideo_DragEnter(object sender, DragEventArgs e)
        {
            if (e.Data.GetDataPresent(DataFormats.FileDrop))
                e.Effect = DragDropEffects.Link;
            else e.Effect = DragDropEffects.None;
        }

        /// <summary>
        /// 窗体大小改变
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void PicVideo_SizeChanged(object sender, EventArgs e)
        {
            Data.PicSize = picVideo.Size;
            if (Data.bitmaps.Count() > 0)
                Data.bitmaps.Clear();
            if (streamDecoder != null && this.WindowState != FormWindowState.Minimized)
            {
                ImageSize = CalculationSize(FrameSize);
                vfc = new VideoFrameConverter(FrameSize, streamDecoder.PixelFormat, ImageSize, AVPixelFormat.AV_PIX_FMT_BGR24);
                //streamDecoder.JumpTime = indexTime;
            }
        }


        /// <summary>
        /// pic重绘
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void PicVideo_Paint(object sender, PaintEventArgs e)
        {
            //if ((streamDecoder == null || vfc == null) || !isPlaying || Data.bitmaps.Count() == 0)
            //    return;
            //Graphics g = e.Graphics;
            //g.Clear(Color.Black);
            //var dic_v = Data.bitmaps.Dequeue();
            //indexTime = dic_v.Key;
            //Bitmap bitmap = dic_v.Value;

            //g.DrawImage(bitmap, rectangle);
        }

        #endregion

        #region 其他方法

        /// <summary>
        /// 计算坐标位置
        /// </summary>
        /// <returns></returns>
        private Point CalculationPoint(ref Size imageSize)
        {
            //图片 长宽比 比 容器长宽比 大 则图片宽度等于容器宽度，反之图片高度等于容器高度
            double imgB = (double)imageSize.Width / (double)imageSize.Height;
            double picB = (double)Data.PicSize.Width / (double)Data.PicSize.Height;
            if (imgB > picB)
            {
                imageSize.Width = Data.PicSize.Width;
                imageSize.Height = (int)(imageSize.Width / imgB);
                return new Point(0, (Data.PicSize.Height - imageSize.Height) / 2);
            }
            else
            {
                imageSize.Height = Data.PicSize.Height;
                imageSize.Width = (int)(imageSize.Height * imgB);
                return new Point((Data.PicSize.Width - imageSize.Width) / 2, 0);
            }
        }


        /// <summary>
        /// 计算应该显示图片的大小
        /// </summary>
        /// <returns></returns>
        private Size CalculationSize(Size imageSize)
        {
            //图片 长宽比 比 容器长宽比 大 则图片宽度等于容器宽度，反之图片高度等于容器高度
            double imgB = (double)imageSize.Width / (double)imageSize.Height;
            double picB = (double)Data.PicSize.Width / (double)Data.PicSize.Height;
            if (imgB > picB)
            {
                imageSize.Width = Data.PicSize.Width;
                if (imageSize.Width % 8 != 0)
                    imageSize.Width -= imageSize.Width % 8;
                imageSize.Height = (int)(imageSize.Width / imgB);
            }
            else
            {
                imageSize.Height = Data.PicSize.Height;
                imageSize.Width = (int)(imageSize.Height * imgB);
                if (imageSize.Width % 8 != 0)
                    imageSize.Width -= imageSize.Width % 8;
                imageSize.Height = (int)(imageSize.Width / imgB);
            }
            return imageSize;
        }

        /// <summary>
        /// 获取当前时间戳 毫秒
        /// </summary>
        /// <returns></returns>
        private long GetTimeStamp()
        {
            return (DateTime.Now.ToUniversalTime().Ticks - 621355968000000000) / 10000;
        }

        #endregion
    }
}
